/*
 * $Id: OnWatchService.java,v 1.32 2012/02/13 17:01:52 navdeep_mahajan Exp $
 * 
 * Copyright (C) 2008 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.onwatch.service;

import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.apache.regexp.RE;
import org.apache.regexp.RESyntaxException;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import com.ge.healthcare.autosc.common.ASCLogger;
import com.ge.healthcare.autosc.common.ASCUtil;
import com.ge.healthcare.autosc.common.database.dao.ICodeDetailsDAO;
import com.ge.healthcare.autosc.common.database.dao.IFileTransferDetailsDAO;
import com.ge.healthcare.autosc.common.database.dao.IOnwatchTaskDAO;
import com.ge.healthcare.autosc.common.database.dao.IRequestDAO;
import com.ge.healthcare.autosc.common.database.dao.IRequestDefn2ProdiagBlockDAO;
import com.ge.healthcare.autosc.common.database.dao.IRequestListDAO;
import com.ge.healthcare.autosc.common.database.dao.IRequestPropertiesDAO;
import com.ge.healthcare.autosc.common.database.dao.IRequestTypeDAO;
import com.ge.healthcare.autosc.common.database.dao.ITaskDAO;
import com.ge.healthcare.autosc.common.database.dao.ITaskTypeDAO;
import com.ge.healthcare.autosc.common.database.pojo.ActFileTransferDetails;
import com.ge.healthcare.autosc.common.database.pojo.ActOnwatchTask;
import com.ge.healthcare.autosc.common.database.pojo.ActRequest;
import com.ge.healthcare.autosc.common.database.pojo.ActRequestList;
import com.ge.healthcare.autosc.common.database.pojo.ActRequestProperties;
import com.ge.healthcare.autosc.common.database.pojo.ActTask;
import com.ge.healthcare.autosc.common.database.pojo.CfgRequestDefn;
import com.ge.healthcare.autosc.common.database.pojo.CfgTaskType;
import com.ge.healthcare.autosc.common.dbLogger.ContactLogger;
import com.ge.healthcare.autosc.common.dbLogger.StepLogger;
import com.ge.healthcare.autosc.common.exceptions.BasicRuntimeException;
import com.ge.healthcare.autosc.common.exceptions.GSCCUtilException;
import com.ge.healthcare.autosc.common.message.sender.MessageSender;
import com.ge.healthcare.autosc.common.pojo.sysInfo.SystemDetails;
import com.ge.healthcare.autosc.common.pojo.sysInfo.SystemInfo;
import com.ge.healthcare.autosc.common.util.ApplicationInfo;
import com.ge.healthcare.autosc.common.util.CheckSumUtil;
import com.ge.healthcare.autosc.common.util.CommonCodeConstants;
import com.ge.healthcare.autosc.common.util.GSCCUtil;
import com.ge.healthcare.autosc.common.util.PropertyConstants;
import com.ge.healthcare.autosc.common.util.RequestDefnLookup;
import com.ge.healthcare.autosc.common.util.TimeFormatStandard;
import com.ge.healthcare.autosc.onwatch.bean.BaseInfoBean;
import com.ge.healthcare.autosc.onwatch.bean.FileDownloadBean;
import com.ge.healthcare.autosc.onwatch.bean.FileUploadBean;
import com.ge.healthcare.autosc.onwatch.bean.HandshakeBean;
import com.ge.healthcare.autosc.onwatch.exception.FileManagerException;
import com.ge.healthcare.autosc.onwatch.exception.OnWatchException;
import com.ge.healthcare.autosc.onwatch.exception.StatusReportHelperException;
import com.ge.healthcare.autosc.onwatch.pojo.OnwatchTaskInfo;
import com.ge.healthcare.autosc.onwatch.util.FileManager;
import com.ge.healthcare.autosc.onwatch.util.OnWatchCodeConstants;
import com.ge.healthcare.autosc.onwatch.util.OnWatchConstants;
import com.ge.healthcare.autosc.onwatch.util.OnWatchLogger;
import com.ge.healthcare.autosc.onwatch.util.OnWatchTaskBuilder;
import com.ge.healthcare.autosc.onwatch.util.StatusReportHelper;
import com.ge.healthcare.autosc.onwatch.xml.tag.StatusReport;

/**
 * 
 * @author 212042946
 * 
 */
public class OnWatchService implements IOnWatchService {

	private IRequestListDAO requestListDAO = null;
	private IRequestTypeDAO requestTypeDAO = null;
	private IRequestDAO requestDAO = null;
	private ITaskDAO taskDAO = null;
	private ICodeDetailsDAO codeDetailsDAO = null;
	private ITaskTypeDAO taskTypeDAO = null;
	private IFileTransferDetailsDAO fileTransferDetailsDAO = null;
	private IOnwatchTaskDAO onwatchTaskDAO = null;
	private IRequestPropertiesDAO requestPropertiesDAO = null;
	private IRequestDefn2ProdiagBlockDAO reqDef2ProDiagBlkDAO = null;
	private MessageSender messageSender = null;
	private ThreadLocal<String> exitCode = new ThreadLocal<String>();
	/* File Name is blocked for transfer */
	public static final String PRC_F_TDS_021 = "PRC_F_TDS_021";

	public void init() {
		Assert.notNull(requestListDAO,
				"ActRequestListDAO is not defined. Use setActRequestListDAO to provide one");
		Assert.notNull(requestTypeDAO,
				"ActRequestTypeDAO is not defined. Use setActRequestTypeDAO to provide one");
		Assert.notNull(requestDAO,
				"ActRequestDAO is not defined. Use setActRequestDAO to provide one");
		Assert.notNull(taskDAO,
				"ActTaskDAO is not defined. Use setActTaskDAO to provide one");
		Assert.notNull(codeDetailsDAO,
				"CodeDetailsDAO is not defined. Use setCodeDetailsDAO to provide one");
		Assert.notNull(taskTypeDAO,
				"taskTypeDAO is not defined. Use setTaskTypeDAO to provide one");
		Assert.notNull(
				fileTransferDetailsDAO,
				"fileTransferDetailsDAO is not defined. Use setActFileTransferDetailsDAO to provide one");
		Assert.notNull(onwatchTaskDAO,
				"onwatchTaskDAO is not defined. Use setActOnwatchTaskDAO to provide one");
		Assert.notNull(messageSender,
				"messageSender is not defined. Use setMessageSender to provide one");
		Assert.notNull(
				requestPropertiesDAO,
				"ActRequestPropertiesDAO object is not defined. "
						+ "Please use setActRequestPropertiesDAO to provide one");
	}

	public void transferIIPHttpClientPkg(String systemId, String linkSecret)
			throws OnWatchException {

	}

	public String getOnwatchTaskList(HandshakeBean handshakeBean)
			throws OnWatchException {
		String methodName = "getOnwatchTaskList";
		String taskListXml = null;
		List<OnwatchTaskInfo> onwatchTaskInfoLst = null;
		try {
			SystemInfo systemInfo = getSystemInfo(handshakeBean.getSystemId(),
					handshakeBean.getLinkSecret());
			ContactLogger.addContactLog(systemInfo.getSystemId(),
					"This is system " + systemInfo.getSystemId());
			// validateSystemInfo(handshakeBean, systemInfo);

			if (!handshakeBean.isRegistrationMode()) {
				onwatchTaskInfoLst = OnWatchTaskBuilder
						.buildOnwatchTaskInfoList(systemInfo);
				onwatchTaskInfoLst = reportInvalidOnwatchTask(onwatchTaskInfoLst);
			} else {
				String msg = "IT IS REGISTRATION!!";
				ContactLogger.addContactLog(msg);
				ASCLogger.info(this.getClass(), methodName, msg);
			}

			OnWatchTaskBuilder builder = new OnWatchTaskBuilder(handshakeBean,
					onwatchTaskInfoLst);
			taskListXml = builder.buildTaskListXml();

			// Report any invalid onwatch task during the time the task XML
			// was built
			reportInvalidOnwatchTask(onwatchTaskInfoLst);
		} catch (OnWatchException owe) {
			ASCLogger.error(this.getClass(), methodName,
					"Get onwatch task failed. Reason: " + owe.getMessage()
							+ "\nRemote request:\n" + handshakeBean, owe);
			releaseOnwatchTasks(onwatchTaskInfoLst);
			throw owe;
		} catch (RuntimeException re) {
			ASCLogger.error(this.getClass(), methodName,
					"Get onwatch task failed because runtimeException. Remote request:\n"
							+ handshakeBean, re);
			releaseOnwatchTasks(onwatchTaskInfoLst);
			throw re;
		} catch (Error r) {
			// Abnormal error occurred, set exit code, log the problem and
			// re-throw it.
			ASCLogger.error(this.getClass(), methodName,
					"Get onwatch task failed because of internal system error",
					r);
			throw r;
		}
		return taskListXml;
	}

	public void fileUploadForExistingTask(FileUploadBean fileUploadBean)
			throws OnWatchException {
		String methodName = "fileUploadForExistingTask";
		exitCode.set(OnWatchCodeConstants.PRC_P_OWR_001);
		try {

			SystemInfo systemInfo = getSystemInfo(fileUploadBean.getSystemId(),
					fileUploadBean.getLinkSecret());
			// validateSystemInfo(fileUploadBean, systemInfo);
			ContactLogger.addContactLog(systemInfo.getSystemId(),
					"This is system " + systemInfo.getSystemId());

			if (ASCUtil.isNullOrEmpty(fileUploadBean.getTaskId())) {
				processFailedStatusReport(fileUploadBean, systemInfo);
			} else {
				processStatusReport(fileUploadBean, systemInfo);
			}
		} catch (OnWatchException owe) {
			ASCLogger.error(this.getClass(), methodName, owe.getMessage(), owe);
			throw owe;
		} catch (RuntimeException re) {
			ASCLogger.error(this.getClass(), methodName,
					"Receive runtime exception", re);
			throw re;
		}
	}

	/**
	 * Process file upload and create new request to post-process the uploaded
	 * files.
	 */
	public void fileUploadForNewTask(FileUploadBean fileUploadBean)
			throws OnWatchException {
		String methodName = "fileUploadForNewTask";
		ActRequest request = null;
		FileManager fileManager = null;
		boolean cutOffPoint = false;
		// success request
		boolean success = false;

		exitCode.set(OnWatchCodeConstants.PRC_P_OWR_001);
		SystemInfo systemInfo = getSystemInfo(fileUploadBean.getSystemId(),
				fileUploadBean.getLinkSecret());
		ContactLogger.addContactLog(systemInfo.getSystemId(), "This is system "
				+ systemInfo.getSystemId());
		// validateSystemInfo(fileUploadBean, systemInfo);
		try {
			ActRequestList requestList = createAutoscRequest(fileUploadBean,
					systemInfo);
			request = requestList.getRequest(0);
			OnWatchLogger.startLogProcessing(request.getTask(0));
			fileManager = getFileManager(requestList);
			performFileUpload(fileUploadBean, fileManager, request);
			// From this forward no exception should be thrown back to
			// controller
			cutOffPoint = true;
			performFileUnpack(fileUploadBean, fileManager, request);
			exitCode.set(OnWatchCodeConstants.PRC_S_OWR_001);
			success = true;
		} catch (OnWatchException owe) {
			exitCode.set(owe.getErrorCode());
			ASCLogger.error(this.getClass(), methodName, owe.getMessage(), owe,
					true);
			if (!cutOffPoint) {
				throw owe;
			}
		} catch (RuntimeException re) {
			// Since no trace for uploaded files, delete them
			exitCode.set(OnWatchCodeConstants.PRC_F_OWR_010);
			ASCLogger.error(this.getClass(), methodName,
					"Receive runtime exception", re, true);
			if (!cutOffPoint) {
				throw re;
			}
		} catch (Error r) {
			// Abnormal error occurred, set exit code, log the problem.
			// Only propagate the error when cut-off point is passed because we
			// do not want the client redo the request.
			exitCode.set(OnWatchCodeConstants.PRC_F_OWR_036);
			ASCLogger.error(this.getClass(), methodName,
					"Internal system error", r, true);
			if (!cutOffPoint) {
				throw r;
			}
		} finally {
			addChronology(fileUploadBean, systemInfo, success);
			uploadComplete(request);
		}
	}

	public File getDownloadFile(FileDownloadBean fileDownloadBean)
			throws OnWatchException {
		String methodName = "getDownloadFile";
		exitCode.set(OnWatchCodeConstants.PRC_P_OWR_002);
		File localFile = null;

		try {
			ActTask task = getActTask(new Long(fileDownloadBean.getTaskId()));
			OnWatchLogger.startLogProcessing(task);
			SystemInfo systemInfo = getSystemInfo(
					fileDownloadBean.getSystemId(),
					fileDownloadBean.getLinkSecret());
			ContactLogger.addContactLog(systemInfo.getSystemId(),
					"This is system " + systemInfo.getSystemId());
			// validateSystemInfo(fileDownloadBean, systemInfo);

			ActRequest request = requestDAO.getByRequestID(task.getRequestId());
			if (request.isSystemMatch(systemInfo) == false) {
				exitCode.set(OnWatchCodeConstants.PRC_F_OWR_011);
				String errorStr = "System requesting the file does not own the task\n"
						+ fileDownloadBean;
				ASCLogger.error(this.getClass(), methodName, errorStr, true);
				throw new OnWatchException(errorStr,
						OnWatchCodeConstants.PRC_F_OWR_011);
			}

			localFile = FileManager.getOnwatchFile(
					fileDownloadBean.getFileName(), task.getRequestId());
			if (localFile == null) {
				exitCode.set(OnWatchCodeConstants.PRC_F_OWR_009);
				String errorStr = "Failed retrieving requested file from the back office.\n"
						+ fileDownloadBean;
				ASCLogger.error(this.getClass(), methodName, errorStr, true);
				throw new OnWatchException(errorStr,
						OnWatchCodeConstants.PRC_F_OWR_009);
			}
			exitCode.set(OnWatchCodeConstants.PRC_S_OWR_002);
		} catch (RuntimeException re) {
			exitCode.set(OnWatchCodeConstants.PRC_F_OWR_010);
			ASCLogger.error(this.getClass(), methodName,
					"Receive runtime exception", re, true);
			throw re;
		} catch (Error r) {
			// Abnormal error occurred, set exit code, log the problem and
			// re-throw it.
			exitCode.set(OnWatchCodeConstants.PRC_F_OWR_036);
			ASCLogger
					.error(this.getClass(),
							methodName,
							"Failed retrieving requested file because of internal system error",
							r, true);
			throw r;
		} finally {
			OnWatchLogger.endLogProcessing(exitCode.get());
		}
		return localFile;
	}

	public void setActRequestListDAO(IRequestListDAO requestListDAO) {
		this.requestListDAO = requestListDAO;
	}

	public void setRequestTypeDAO(IRequestTypeDAO requestTypeDAO) {
		this.requestTypeDAO = requestTypeDAO;
	}

	public void setActRequestDAO(IRequestDAO requestDAO) {
		this.requestDAO = requestDAO;
	}

	public void setActTaskDAO(ITaskDAO taskDAO) {
		this.taskDAO = taskDAO;
	}

	public void setCodeDetailsDAO(ICodeDetailsDAO codeDetailsDAO) {
		this.codeDetailsDAO = codeDetailsDAO;
	}

	public void setTaskTypeDAO(ITaskTypeDAO taskTypeDAO) {
		this.taskTypeDAO = taskTypeDAO;
	}

	public void setActRequestPropertiesDAO(
			IRequestPropertiesDAO requestPropertiesDAO) {
		this.requestPropertiesDAO = requestPropertiesDAO;
	}

	/**
	 * Set JMS message sender
	 */
	public void setMessageSender(MessageSender messageSender) {
		this.messageSender = messageSender;
	}

	public void setActFileTransferDetailsDAO(
			IFileTransferDetailsDAO fileTransferDetailsDAO) {
		this.fileTransferDetailsDAO = fileTransferDetailsDAO;
	}

	public void setActOnwatchTaskDAO(IOnwatchTaskDAO onwatchTaskDAO) {
		this.onwatchTaskDAO = onwatchTaskDAO;
	}

	private void processStatusReport(FileUploadBean fileUploadBean,
			SystemInfo systemInfo) throws OnWatchException {
		String methodName = "processStatusReport";
		ActRequest request = null;
		FileManager fileManager = null;
		File requestDir = null;
		ActTask task = null;
		// Determine if we should throw exception back to controller
		boolean cutOffPoint = false;
		// Successful request
		boolean success = false;

		task = getActTask(new Long(fileUploadBean.getTaskId()));
		// If task has moved on, nothing can be done from the client side
		if (!task.hasTaskCode(codeDetailsDAO,
				OnWatchCodeConstants.PRC_P_OWR_003)) {
			System.out.println("Task has code: " + task.getCodeId());
			ContactLogger.addContactLog("OnWatch task has been closed.");
			return;
		}
		OnWatchLogger.startLogProcessing(task);
		request = requestDAO.getByRequestID(task.getRequestId());
		request.addActTask(task);
		// If system does not own the task, someone may initiate request for
		// other task.
		// This is a unlikely scenario
		if (request.isSystemMatch(systemInfo) == false) {
			String error = "System does not own the task!!";
			ContactLogger.addContactLog(error);
			throw new OnWatchException(error,
					OnWatchCodeConstants.PRC_F_OWR_011);
		}
		ActRequestList requestList = requestListDAO.getRequestListByID(request
				.getRequestListId());
		requestList.addRequest(request);

		try {
			fileManager = getFileManager(requestList);
			// First put the file into back office
			performFileUpload(fileUploadBean, fileManager, request);
			cutOffPoint = true;
			performFileUnpack(fileUploadBean, fileManager, request);
			requestDir = getRequestDir(fileManager,
					requestList.getRequestListId(), request.getRequestId());
			StatusReport statusReport = StatusReportHelper
					.getStatusReport(requestDir);
			processStatusReport(fileUploadBean.getTaskId(), statusReport);
			success = true;
		} catch (OnWatchException owe) {
			exitCode.set(owe.getErrorCode());
			ASCLogger.error(this.getClass(), methodName,
					"Error processing status report", owe, true);
			if (!cutOffPoint) {
				throw owe;
			}
		} catch (StatusReportHelperException srhe) {
			exitCode.set(srhe.getErrorCode());
			ASCLogger.error(this.getClass(), methodName,
					"Error processing status report", srhe, true);
			if (!cutOffPoint) {
				throw new OnWatchException("Error processing status report",
						srhe, srhe.getErrorCode());
			}
		} catch (RuntimeException re) {
			exitCode.set(OnWatchCodeConstants.PRC_F_OWR_010);
			StringBuffer errorStr = new StringBuffer()
					.append("Error processing status report.");
			ASCLogger.error(this.getClass(), methodName, errorStr.toString(),
					re, true);
			if (!cutOffPoint) {
				throw re;
			}
		} catch (Error r) {
			// Abnormal error occurred, set exit code and log the problem.
			// If it passes the cut-off point, we do not propagate the error
			// because
			// we do not want to report error to client and have them send this
			// again.
			exitCode.set(OnWatchCodeConstants.PRC_F_OWR_036);
			ASCLogger
					.error(this.getClass(),
							methodName,
							"Error processing status report because of internal system error",
							r, true);
			if (!cutOffPoint) {
				throw r;
			}
		} finally {
			addChronology(fileUploadBean, systemInfo, success);
			/*
			 * Only want to proceed the task when it is either pass the cut off
			 * point, or this is the last upload attempt.
			 */
			if ((cutOffPoint) || (fileUploadBean.isLastAttempt())) {
				try {
					uploadComplete(request);
				} catch (RuntimeException re) {
					ASCLogger.error(this.getClass(), methodName,
							"Error marking the task as complete", true);
				}
			}
		}
	}

	private void processFailedStatusReport(FileUploadBean fileUploadBean,
			SystemInfo systemInfo) throws OnWatchException {
		String methodName = "processFailedStatusReport";
		FileManager fileManager = null;
		ActRequest request = null;
		boolean cutOffPoint = false;
		boolean success = false;

		try {
			ActRequestList requestList = createAutoscRequest(fileUploadBean,
					systemInfo);
			request = requestList.getRequest(0);
			OnWatchLogger.startLogProcessing(request.getTask(0));
			fileManager = getFileManager(requestList);
			performFileUpload(fileUploadBean, fileManager, request);
			// as long as upload is succeeded, it is AutoSC business
			cutOffPoint = true;
			performFileUnpack(fileUploadBean, fileManager, request);
			File requestDir = getRequestDir(fileManager,
					requestList.getRequestListId(), request.getRequestId());
			StatusReport statusReport = StatusReportHelper
					.getStatusReport(requestDir);
			String failedReason = StatusReportHelper
					.getTaskListStatus(statusReport);
			OnWatchLogger.addStepLog("Onwatch task failed reason: "
					+ failedReason);
			exitCode.set(OnWatchCodeConstants.PRC_S_OWR_001);
			success = true;
		} catch (OnWatchException owe) {
			exitCode.set(owe.getErrorCode());
			ASCLogger.error(this.getClass(), methodName,
					"Error creating requset for failed status report", owe,
					true);
			if (!cutOffPoint) {
				throw owe;
			}
		} catch (RuntimeException re) {
			exitCode.set(OnWatchCodeConstants.PRC_F_OWR_010);
			ASCLogger
					.error(this.getClass(), methodName,
							"Error creating request for failed status report",
							re, true);
			if (!cutOffPoint) {
				throw re;
			}
		} catch (Error r) {
			// Abnormal error occurred, set exit code and log the problem.
			// If it passes the cut-off point, we do not propagate the error
			// because
			// we do not want to report error to client and have them send this
			// again.
			exitCode.set(OnWatchCodeConstants.PRC_F_OWR_036);
			ASCLogger
					.error(this.getClass(),
							methodName,
							"Error creating request for failed status report because of internal system error",
							r, true);
			if (!cutOffPoint) {
				throw r;
			}
		} finally {
			addChronology(fileUploadBean, systemInfo, success);
			if ((cutOffPoint) || (fileUploadBean.isLastAttempt())) {
				try {
					uploadComplete(request);
				} catch (RuntimeException re) {
					ASCLogger.error(this.getClass(), methodName,
							"Error marking the task as complete", true);
				}
			}
		}
	}

	private SystemInfo getSystemInfo(String systemId, String linkSecret)
			throws OnWatchException {
		SystemDetails systemDetails = null;
		SystemInfo systemInfo = null;
		try {
			systemDetails = GSCCUtil.getSystemDetailsForSystemId(systemId);
			if (systemDetails == null) {
				StringBuffer errorStr = new StringBuffer().append(
						"Cannot find system for systemId ").append(systemId);
				throw new OnWatchException(errorStr.toString(),
						OnWatchCodeConstants.PRC_F_OWR_001);
			}
			systemInfo = new SystemInfo(systemDetails);
			String sysLinkSecret = systemInfo.getLinkSecret();
			if (ASCUtil.isNullOrEmpty(sysLinkSecret)
					|| (!sysLinkSecret.equals(linkSecret))) {
				String msg = "Cannot retrieve the system information. "
						+ "System may not be properly checked out.";
				throw new OnWatchException(msg,
						OnWatchCodeConstants.PRC_F_OWR_002);
			}
		} catch (GSCCUtilException gue) {
			String msg = "Error retrieving system information for system "
					+ systemId;
			throw new OnWatchException(msg, gue,
					OnWatchCodeConstants.PRC_F_OWR_002);
		}
		return systemInfo;
	}

	// We do not need the model type
	/*
	 * private void validateSystemInfo(BaseInfoBean bean, SystemInfo systemInfo)
	 * throws OnWatchException { StringBuffer msg = new StringBuffer();
	 * if(!bean.getMln().equalsIgnoreCase(systemInfo.getMln())) {
	 * msg.append("The mln is not the same as the equivalent in the back office"
	 * ); } else
	 * if(!bean.getModelType().equalsIgnoreCase(systemInfo.getModelType())) {
	 * msg
	 * .append("The model type is not the same as the equivalent in the back office"
	 * ); }
	 * 
	 * if(!ASCUtil.isNullOrEmptyStringBuffer(msg)) { throw new
	 * OnWatchException(msg.toString(), OnWatchCodeConstants.PRC_F_OWR_019); } }
	 */
	private FileManager getFileManager(ActRequestList requestList)
			throws OnWatchException {
		FileManager fileManager = null;
		try {
			fileManager = FileManager.buildFileStructure(requestList);
		} catch (FileManagerException fme) {
			throw new OnWatchException(fme.getMessage(), fme,
					OnWatchCodeConstants.PRC_F_OWR_005);
		}
		return fileManager;
	}

	/**
	 * Checks to see if file transfer is allowed or not
	 * 
	 * @see com.ge.healthcare.autosc.onwatch.service.IOnWatchService#isAllowedForTransfer(java.lang.String,
	 *      java.lang.String, java.lang.String, java.lang.String,
	 *      java.lang.String)
	 */
	public boolean isAllowedForTransfer(String systemId, String requestType,
			String fileName, String serviceId, String createdBy) {
		SystemDetails systemDetails = null;
		SystemInfo systemInfo = null;
		try {
			systemDetails = GSCCUtil.getSystemDetailsForSystemId(systemId);
			if (ASCUtil.isNullOrEmptyObject(systemDetails)) {
				return false;
			}
			systemInfo = new SystemInfo(systemDetails);
			boolean checkEntitlement = ApplicationInfo
					.getInstance()
					.getBooleanProperty(
							PropertyConstants.PERFORM_ENTITLEMENT_CHECK, "true");
			if (checkEntitlement && !systemInfo.isEntitled()) {
				ASCLogger.info(this.getClass(), "isAllowedForTransfer",
						"Denying access to unentitled system:" + systemInfo);
				return false;
			}
		} catch (GSCCUtilException gsccExcep) {
			ASCLogger.error(this.getClass(), "isAllowedForTransfer",
					"Failed to system details for:" + systemId, gsccExcep);
			return false;
		}
		CfgRequestDefn requestDefn = RequestDefnLookup.lookup(requestType,
				systemInfo.getModality(), systemInfo.getModelType(), serviceId,
				createdBy);
		if (ASCUtil.isNullOrEmptyObject(requestDefn)) {
			return false;
		}
		List<String> blockLst = reqDef2ProDiagBlkDAO
				.getProDiagBlockLstForRequestDefn(requestDefn
						.getRequestDefnId(), systemInfo.getIsoCode());
		if (ASCUtil.isNullOrEmptyList(blockLst)) {
			ASCLogger.info(this.getClass(), "isAllowedForTransfer",
					"No Block list specified for request definition id:"
							+ requestDefn.getRequestDefnId());
			return true;
		}
		for (String pattern : blockLst) {
			try {
				RE reFilePattern = new RE(pattern);
				if (reFilePattern.match(fileName)) {
					this.createBlockRecord(systemId, requestType, fileName,
							serviceId, createdBy, systemInfo);
					return false;
				}
			} catch (RESyntaxException syntaxException) {
				ASCLogger.info(this.getClass(), "isAllowedForTransfer", String
						.format("Pattern %s is invalid and is being ignored",
								pattern));
			}
		}
		return true;
	}

	/**
	 * Creates the record for the blocked file
	 * 
	 * @param systemId
	 * @param requestType
	 * @param fileName
	 * @param serviceId
	 * @param createdBy
	 * @param systemInfo
	 */
	private void createBlockRecord(String systemId, String requestType,
			String fileName, String serviceId, String createdBy,
			SystemInfo systemInfo) {
		FileUploadBean fileUploadBean = new FileUploadBean();
		fileUploadBean.setSystemId(systemId);
		fileUploadBean.setRequestType(requestType);
		fileUploadBean.setFilename(fileName);
		fileUploadBean.setServiceId(serviceId);
		fileUploadBean.setCreatedBy(createdBy);
		ActRequestList requestList = createRequestList(fileUploadBean);
		createRequest(fileUploadBean, requestList, systemInfo);
		ActTask actTask = createTask(requestList.getRequest(0), PRC_F_TDS_021);
		messageSender.sendToQueue(actTask);
	}

	/*
	 * Create new AutoSC request. This function opens a new transaction
	 */
	public ActRequestList createAutoscRequest(FileUploadBean fileUploadBean,
			SystemInfo systemInfo) {

		ActRequestList requestList = createRequestList(fileUploadBean);
		createRequest(fileUploadBean, requestList, systemInfo);
		createTask(requestList.getRequest(0), null);
		return requestList;
	}

	/*
	 * Create new request list object. The object does not binded to Hibernate
	 * session
	 */
	private ActRequestList createRequestList(FileUploadBean fileUploadBean) {

		Long batchId = requestListDAO.getNewBatchId();
		ActRequestList requestList = new ActRequestList();
		requestList.setBatchId(batchId);
		requestList.setRequestTypeId(requestTypeDAO.getRequestTypeByName(
				fileUploadBean.getRequestType()).getRequestTypeId());
		requestList.setEnterDate();
		requestList.setScheduledDate();
		ASCLogger.debug(this.getClass(), "createRequestList", "RequestList: "
				+ requestList);
		return requestList;
	}

	/*
	 * Create new request object. The object does not binded to Hibernate
	 * session
	 */
	private void createRequest(FileUploadBean fileUploadBean,
			ActRequestList requestList, SystemInfo systemInfo) {
		// Build request
		ActRequest request = new ActRequest(systemInfo);
		request.setRequestCode(codeDetailsDAO,
				CommonCodeConstants.REQ_S_NEW_000);
		CfgRequestDefn requestDefn = RequestDefnLookup.lookup(
				fileUploadBean.getRequestType(), request.getModality(),
				request.getModelType(), fileUploadBean.getServiceId(),
				fileUploadBean.getCreatedBy());
		if (requestDefn != null) {
			// The reason why we are saving here is so that in case there is no
			// request definition available for a request then request list
			// shouldn't be saved.
			requestListDAO.save(requestList);
			request.setRequestListId(requestList.getRequestListId());
			request.setRequestDefn(requestDefn);
			request.setRequestDefnId(requestDefn.getRequestDefnId());
		} else {
			StringBuffer errorStr = new StringBuffer()
					.append("Cannot find request definition.\n")
					.append("{requestType: ")
					.append(fileUploadBean.getRequestType()).append("},")
					.append("{modality: ").append(request.getModality())
					.append("},").append("{modelType: ")
					.append(request.getModelType()).append("},")
					.append("{serviceId: ")
					.append(fileUploadBean.getServiceId()).append("},")
					.append("{createdBy: ")
					.append(fileUploadBean.getCreatedBy()).append("}");
			throw new BasicRuntimeException(errorStr.toString());
		}
		requestDAO.save(request);

		createRequestProperties(request, fileUploadBean);

		ASCLogger
				.debug(this.getClass(), "createRequest", "request: " + request);
		requestList.addRequest(request);
	}

	private void createRequestProperties(ActRequest request,
			FileUploadBean fileUploadBean) {
		if (!ASCUtil.isNullOrEmpty(fileUploadBean.getServiceId())) {
			requestPropertiesDAO.addRequestProperties(request.getRequestId(),
					ActRequestProperties.SERVICE_ID,
					fileUploadBean.getServiceId());
		}
		if (!ASCUtil.isNullOrEmpty(fileUploadBean.getCreatedBy())) {
			requestPropertiesDAO.addRequestProperties(request.getRequestId(),
					ActRequestProperties.CREATED_BY,
					fileUploadBean.getCreatedBy());
		}
	}

	/*
	 * Mark the task as complete and send the task out to dispatcher
	 */
	private void uploadComplete(ActRequest request) {
		if (request != null) {
			List<ActTask> tasks = request.getActTasks();
			for (ActTask task : tasks) {
				ASCLogger.info(this.getClass(), "uploadComplete",
						"complete code: " + exitCode.get());
				task.setTaskCode(codeDetailsDAO, exitCode.get());
				task.setEndTime();
				taskDAO.save(task);
				messageSender.sendToQueue(task);
				OnWatchLogger.endLogProcessing(exitCode.get());
			}
		}
	}

	private ActTask createTask(ActRequest request, String codeName) {
		ActTask task = new ActTask();
		task.setRequestId(request.getRequestId());
		task.setFailedCount(new Long("0"));
		task.setStepTypeId(StepLogger.getStepTypeId());
		task.setTaskType(getTaskTypeName(request.getRequestDefn()));
		task.setExternalAutoSC();
		task.setPriority(new Long("1"));
		task.setExpireDate();
		if (ASCUtil.isNullOrEmpty(codeName)) {
			task.setTaskCode(codeDetailsDAO, exitCode.get());
		} else {
			task.setTaskCode(codeDetailsDAO, codeName);
		}
		task.setStartTime();
		task.setEndTime();
		taskDAO.save(task);
		ASCLogger.debug(this.getClass(), "createTask", "Task: " + task);
		request.addActTask(task);
		return task;
	}

	/*
	 * Create FileTransferDetails record for files in the directory where the
	 * given file is located
	 */
	private void createFileTransferDetails(ActRequest request, File uploadFile,
			boolean exclusive, Date startDate, boolean status, boolean internal)
			throws OnWatchException {
		if (uploadFile.exists()) {
			File requestDir = uploadFile.getParentFile();
			File[] files = requestDir.listFiles();
			for (int idx = 0; idx < files.length; idx++) {
				File file = files[idx];
				if ((file.getName().equals(uploadFile.getName()))
						&& (exclusive)) {
					continue;
				}
				ASCLogger.debug(this.getClass(), "createFileTransferDetails",
						"Create record for file: " + file.getPath());
				createFileTransferDetails(request, file, startDate, status,
						true, internal);
			}
		}
	}

	/*
	 * Create FileTransferDetails record. The object does not binded to
	 * Hibernate session
	 */
	private void createFileTransferDetails(ActRequest request, File file,
			Date startDate, boolean status, boolean checkSum, boolean internal)
			throws OnWatchException {
		ActFileTransferDetails ftDetails = new ActFileTransferDetails();
		ftDetails.setRequestId(request.getRequestId());
		ftDetails.setGetDirection();
		ftDetails.setFileName(file.getName());
		ftDetails.setFileDir(file.getParent());
		ftDetails.setStartTime(startDate);
		ftDetails.setContentSize(file.length());
		ftDetails.setEndTime(TimeFormatStandard.getNowMillis());
		ftDetails.setInternalCommFile(internal);
		if (status == true) {
			ftDetails.setCompleted();
		} else {
			ftDetails.setFail();
		}
		if (checkSum) {
			try {
				ftDetails.setCheckSum(CheckSumUtil.getMD5Checksum(file
						.getAbsolutePath()));
			} catch (Exception e) {
				String errorStr = "Failed create checksum for file "
						+ file.getAbsolutePath();
				throw new OnWatchException(errorStr, e,
						OnWatchCodeConstants.PRC_F_OWR_007);
			}
		}
		request.addActFileTransferDetails(ftDetails);
		fileTransferDetailsDAO.save(ftDetails);
	}

	private String getTaskTypeName(CfgRequestDefn requestDefn) {
		String taskTypeName = null;
		if (requestDefn != null) {
			CfgTaskType taskType = taskTypeDAO.getByID(requestDefn
					.getInitialTaskId());
			if (taskType != null) {
				taskTypeName = taskType.getTaskTypeName();
			}
		}
		return taskTypeName;
	}

	/*
	 * Perform file upload to the back office. The method accomplishes two tasks
	 * <p> <ol> <li> Upload .tar.gz file to the back office</li> <li> Exploded
	 * the file</li> </ol>
	 */
	private void performFileUpload(FileUploadBean fileUploadBean,
			FileManager fileManager, ActRequest request)
			throws OnWatchException {
		String methodName = "performFileUpload";
		boolean fileUploadStatus = false;
		OnWatchLogger.addStepLog("Upload file size: "
				+ fileUploadBean.getFileSize());
		MultipartFile file = fileUploadBean.getFile();
		if (file != null) {
			File destFile = null;
			try {
				File destDir = getRequestDir(fileManager,
						request.getRequestListId(), request.getRequestId());
				destFile = new File(destDir, file.getOriginalFilename());
				putFile(file, destFile);
				// check file data integrity
				String checkSum = CheckSumUtil.getMD5Checksum(destFile
						.getAbsolutePath());

				if (!checkSum.equals(fileUploadBean.getChecksum())) {
					String error = "File checksum does not match with file "
							+ destFile.getPath();
					throw new OnWatchException(error,
							OnWatchCodeConstants.PRC_F_OWR_028);
				}
				fileUploadStatus = true;
				OnWatchLogger.addStepLog("File " + destFile.getPath()
						+ " is successfully uploaded");
			} catch (NoSuchAlgorithmException nsae) {
				throw new OnWatchException(
						"Unable to perform checksum for file "
								+ destFile.getPath(), nsae,
						OnWatchCodeConstants.PRC_F_OWR_007);
			} catch (IOException ioe) {
				throw new OnWatchException(
						"Unable to perform checksum for file "
								+ destFile.getPath(), ioe,
						OnWatchCodeConstants.PRC_F_OWR_007);
			} finally {
				// Create entries for files in the directory including the
				// uploaded file
				createFileTransferDetails(request, destFile,
						fileUploadBean.getStartDate(), fileUploadStatus, true,
						true);
			}
		} else {
			ASCLogger.info(
					this.getClass(),
					methodName,
					new StringBuffer().append("No upload file.\n")
							.append("systemId: ")
							.append(fileUploadBean.getSystemId()).append("\n"),
					true);
		}
	}

	private void performFileUnpack(FileUploadBean fileUploadBean,
			FileManager fileManager, ActRequest request)
			throws OnWatchException {
		MultipartFile file = fileUploadBean.getFile();
		boolean fileUnpackStatus = false;
		if (file != null) {
			File destFile = null;
			try {
				File destDir = getRequestDir(fileManager,
						request.getRequestListId(), request.getRequestId());
				destFile = new File(destDir, file.getOriginalFilename());
				unpackFile(destFile);
				fileUnpackStatus = true;
				OnWatchLogger.addStepLog("File " + destFile.getPath()
						+ " is successfully unpacked");
			} finally {
				// Create entries for files in the directory including the
				// uploaded file
				createFileTransferDetails(request, destFile, true,
						fileUploadBean.getStartDate(), fileUnpackStatus, false);
			}
		}
	}

	private File getRequestDir(FileManager fileManager, Long requestListId,
			Long requestId) throws OnWatchException {
		File requestDir = null;
		try {
			requestDir = fileManager
					.getRequestDirFile(requestListId, requestId);
		} catch (FileManagerException fme) {
			String error = "Error retrieving back office directory. Request Id:\n"
					+ requestId;
			throw new OnWatchException(error.toString(), fme,
					OnWatchCodeConstants.PRC_F_OWR_012);
		}
		return requestDir;
	}

	private void putFile(MultipartFile file, File destFile)
			throws OnWatchException {
		try {
			// Place file into back office
			FileManager.putFile(file, destFile);
			OnWatchLogger.addStepLog("Successfully upload file as "
					+ destFile.getPath());
		} catch (FileManagerException fme) {
			String errorStr = "Failed uploading file to back office:\n"
					+ "{remoteFile:=" + file.getOriginalFilename()
					+ "},{localFile=" + destFile.getAbsolutePath() + "}";
			throw new OnWatchException(errorStr, fme,
					OnWatchCodeConstants.PRC_F_OWR_003);
		}
	}

	private void unpackFile(File destFile) throws OnWatchException {
		try {
			FileManager.unpackFile(destFile);
			OnWatchLogger.addStepLog("Successfully unpack file "
					+ destFile.getPath());
		} catch (FileManagerException fme) {
			String errorStr = "Failed extracting uploaded file "
					+ destFile.getAbsolutePath();
			throw new OnWatchException(errorStr, fme,
					OnWatchCodeConstants.PRC_F_OWR_004);
		}
	}

	private void processStatusReport(String onwatchTaskId,
			StatusReport statusReport) throws OnWatchException {
		// Check to make sure task Id in the report matches the task
		String taskResultIdStr = StatusReportHelper
				.getTaskResultId(statusReport);
		if (taskResultIdStr != null) {
			if (!taskResultIdStr.equals(onwatchTaskId)) {
				// CAUTIOUS! Different task Id in the statusReport could cause
				// discrepancy in the AutoSC when AutoSC closes onwatchTask
				// while working on different ActTask. Abandon all changes.
				StringBuffer errorStr = new StringBuffer()
						.append("Task Id in the statusReport does not match ")
						.append("what the request claims itself.")
						.append("{statusReport taskId=")
						.append(taskResultIdStr).append("},")
						.append("{onwatch request onwatchTaskId=")
						.append(onwatchTaskId).append("}").append("\n")
						.append(statusReport);
				ASCLogger.fatal(this.getClass(), "processStatusReport",
						errorStr.toString(), true);
				throw new OnWatchException(errorStr.toString(),
						OnWatchCodeConstants.PRC_F_OWR_017);
			}
		}
		StatusReportHelper.updateOnwatchTask(statusReport);
		if (StatusReportHelper.isTaskSuccess(statusReport) == true) {
			exitCode.set(OnWatchCodeConstants.PRC_S_OWR_003);
		} else {
			exitCode.set(OnWatchCodeConstants.PRC_F_OWR_016);
		}
	}

	private List<OnwatchTaskInfo> reportInvalidOnwatchTask(
			List<OnwatchTaskInfo> onwatchTaskInfoLst) {
		List<OnwatchTaskInfo> newOnwatchTaskInfoLst = new Vector<OnwatchTaskInfo>();
		if (!ASCUtil.isNullOrEmptyList(onwatchTaskInfoLst)) {
			for (OnwatchTaskInfo onwatchTaskInfo : onwatchTaskInfoLst) {
				ActOnwatchTask onwatchTask = onwatchTaskInfo.getOnwatchTask();
				// If task is invalid, send the task to dispatcher.
				if (onwatchTask.isInvalid()) {
					ActTask actTask = taskDAO.getTaskByID(onwatchTask
							.getTaskId());
					actTask.setTaskCode(codeDetailsDAO,
							onwatchTaskInfo.getFailReasonCode());
					messageSender.sendToQueue(actTask);
				} else {
					newOnwatchTaskInfoLst.add(onwatchTaskInfo);
				}
			}
		}
		return newOnwatchTaskInfoLst;
	}

	private ActTask getActTask(Long taskId) throws OnWatchException {
		String methodName = "getActTask";
		boolean invalid = true;
		ActTask task = null;
		ActOnwatchTask onwatchTask = onwatchTaskDAO.getByOnwatchTaskID(taskId);
		if (onwatchTask != null) {
			task = taskDAO.getTaskByID(onwatchTask.getTaskId());
			if (task != null) {
				invalid = false;
			} else {
				ASCLogger.error(this.getClass(), methodName,
						"Cannot find task for onwatchTask Id " + taskId);
			}
		} else {
			ASCLogger.error(this.getClass(), "methodName",
					"Cannot find onwatch task with onwatchTask Id " + taskId);
		}

		if (invalid) {
			String error = "Invalid onwatchTask Id: " + taskId;
			throw new OnWatchException(error,
					OnWatchCodeConstants.PRC_F_OWR_008);
		}
		return task;
	}

	/**
	 * Release the onwatch task being reserved for this operation.
	 * 
	 * @param onwatchTaskInfoLst
	 */
	private void releaseOnwatchTasks(List<OnwatchTaskInfo> onwatchTaskInfoLst) {
		if (!ASCUtil.isNullOrEmptyList(onwatchTaskInfoLst)) {
			Long onwatchTaskListId = onwatchTaskInfoLst.get(0).getOnwatchTask()
					.getOnwatchTaskListId();
			// Call a new transaction
			onwatchTaskDAO.releaseOnwatchTaskByTaskListId(onwatchTaskListId);
		}
	}

	private void addChronology(BaseInfoBean bean, SystemInfo systemInfo,
			boolean success) {
		Map<String, String> chronologyMap = new HashMap<String, String>();
		chronologyMap.put(GSCCUtil.APPLICATION, OnWatchConstants.ONWATCH + "-"
				+ bean.getRequestType());
		chronologyMap.put(GSCCUtil.ATTEMPT_NUM, "1");
		chronologyMap.put(GSCCUtil.COMMENT, this.createChronologyComment(bean));
		String completionCode = "Failure";
		if (success) {
			completionCode = "Success";
		}
		chronologyMap.put(GSCCUtil.COMPLETION_CODE, completionCode);
		chronologyMap.put(GSCCUtil.LOGIN_ID, "asc_acct");
		chronologyMap.put(GSCCUtil.PLATFORM, "AutoSC");
		chronologyMap.put(GSCCUtil.SYSTEM_ID, systemInfo.getSystemId());
		chronologyMap.put(GSCCUtil.TYPE_ID, "Inbound");
		GSCCUtil.addChronology(chronologyMap);
	}

	private String createChronologyComment(BaseInfoBean bean) {
		StringBuffer comment = new StringBuffer();
		if (bean instanceof FileUploadBean) {
			comment.append(((FileUploadBean) bean).toString());
		} else if (bean instanceof FileDownloadBean) {
			comment.append(((FileDownloadBean) bean).toString());
		} else if (bean instanceof HandshakeBean) {
			comment.append(((HandshakeBean) bean).toString());
		}
		return comment.toString();
	}

	/**
	 * @return the reqDef2ProDiagBlkDAO
	 */
	public IRequestDefn2ProdiagBlockDAO getReqDef2ProDiagBlkDAO() {
		return reqDef2ProDiagBlkDAO;
	}

	/**
	 * @param reqDef2ProDiagBlkDAO
	 *            the reqDef2ProDiagBlkDAO to set
	 */
	public void setReqDef2ProDiagBlkDAO(
			IRequestDefn2ProdiagBlockDAO reqDef2ProDiagBlkDAO) {
		this.reqDef2ProDiagBlkDAO = reqDef2ProDiagBlkDAO;
	}
}
